/*
   Copyright (c) 2006-2012 Red Hat, Inc. <http://www.redhat.com>
   This file is part of GlusterFS.

   This file is licensed to you under your choice of the GNU Lesser
   General Public License, version 3 or any later version (LGPLv3 or
   later), or the GNU General Public License, version 2 (GPLv2), in all
   cases as published by the Free Software Foundation.
*/
#include "trash.h"
#include "trash-mem-types.h"
#include <glusterfs/syscall.h>

#define root_gfid                                                              \
    (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
#define trash_gfid                                                             \
    (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 }
#define internal_op_gfid                                                       \
    (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 }

int32_t
trash_truncate_writev_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                          int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
                          struct iatt *postbuf, dict_t *xdata);

int32_t
trash_truncate_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                         int32_t op_ret, int32_t op_errno, inode_t *inode,
                         struct iatt *stbuf, struct iatt *preparent,
                         struct iatt *postparent, dict_t *xdata);

int32_t
trash_unlink_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, struct iatt *buf,
                        struct iatt *preoldparent, struct iatt *postoldparent,
                        struct iatt *prenewparent, struct iatt *postnewparent,
                        dict_t *xdata);
/* Common routines used in this translator */

/**
 * When a directory/file is created under trash directory, it should have
 * the same permission as before. This function will fetch permission from
 * the existing directory and returns the same
 */
mode_t
get_permission(char *path)
{
    mode_t mode = 0755;
    struct stat sbuf = {
        0,
    };
    struct iatt ibuf = {
        0,
    };
    int ret = 0;

    ret = sys_stat(path, &sbuf);
    if (!ret) {
        iatt_from_stat(&ibuf, &sbuf);
        mode = st_mode_from_ia(ibuf.ia_prot, ibuf.ia_type);
    } else
        gf_log("trash", GF_LOG_DEBUG,
               "stat on %s failed"
               " using default",
               path);
    return mode;
}

/**
 * For normalization, trash directory name is stored inside priv structure as
 * '/trash_directory/'. As a result the trailing and leading slashes are being
 * striped out for additional usage.
 */
int
extract_trash_directory(char *priv_value, const char **trash_directory)
{
    char *tmp = NULL;
    int ret = 0;

    GF_VALIDATE_OR_GOTO("trash", priv_value, out);

    tmp = gf_strdup(priv_value + 1);
    if (!tmp) {
        ret = ENOMEM;
        goto out;
    }
    if (tmp[strlen(tmp) - 1] == '/')
        tmp[strlen(tmp) - 1] = '\0';
    *trash_directory = gf_strdup(tmp);
    if (!(*trash_directory)) {
        ret = ENOMEM;
        goto out;
    }
out:
    if (tmp)
        GF_FREE(tmp);
    return ret;
}

/**
 * The trash directory path should be append at beginning of file path for
 * delete or truncate operations. Normal trashing moves the contents to
 * trash directory and trashing done by internal operations are moved to
 * internal_op directory inside trash.
 */
void
copy_trash_path(const char *priv_value, gf_boolean_t internal, char *path,
                size_t path_size)
{
    char trash_path[PATH_MAX] = {
        0,
    };

    strncpy(trash_path, priv_value, sizeof(trash_path));
    trash_path[sizeof(trash_path) - 1] = 0;
    if (internal)
        strncat(trash_path, "internal_op/",
                sizeof(trash_path) - strlen(trash_path) - 1);

    strncpy(path, trash_path, path_size);
    path[path_size - 1] = 0;
}

/**
 * This function performs the reverse operation of copy_trash_path(). It gives
 * out a pointer, whose starting value will be the path inside trash directory,
 * similar to original path.
 */
void
remove_trash_path(const char *path, gf_boolean_t internal, char **rem_path)
{
    if (rem_path == NULL) {
        return;
    }

    *rem_path = strchr(path + 1, '/');
    if (internal)
        *rem_path = strchr(*rem_path + 1, '/');
}

/**
 * Checks whether the given path reside under the specified eliminate path
 */
int
check_whether_eliminate_path(trash_elim_path *trav, const char *path)
{
    int match = 0;

    while (trav) {
        if (strncmp(path, trav->path, strlen(trav->path)) == 0) {
            match++;
            break;
        }
        trav = trav->next;
    }
    return match;
}

/**
 * Stores the eliminate path into internal eliminate path structure
 */
int
store_eliminate_path(char *str, trash_elim_path **eliminate)
{
    trash_elim_path *trav = NULL;
    char *component = NULL;
    char elm_path[PATH_MAX] = {
        0,
    };
    int ret = 0;
    char *strtokptr = NULL;

    if ((str == NULL) || (eliminate == NULL)) {
        ret = EINVAL;
        goto out;
    }

    component = strtok_r(str, ",", &strtokptr);
    while (component) {
        trav = GF_CALLOC(1, sizeof(*trav), gf_trash_mt_trash_elim_path);
        if (!trav) {
            ret = ENOMEM;
            goto out;
        }
        if (component[0] == '/')
            sprintf(elm_path, "%s", component);
        else
            sprintf(elm_path, "/%s", component);

        if (component[strlen(component) - 1] != '/')
            strncat(elm_path, "/", sizeof(elm_path) - strlen(elm_path) - 1);

        trav->path = gf_strdup(elm_path);
        if (!trav->path) {
            ret = ENOMEM;
            gf_log("trash", GF_LOG_DEBUG, "out of memory");
            GF_FREE(trav);
            goto out;
        }
        trav->next = *eliminate;
        *eliminate = trav;
        component = strtok_r(NULL, ",", &strtokptr);
    }
out:
    return ret;
}

/**
 * Appends time stamp to given string
 */
void
append_time_stamp(char *name, size_t name_size)
{
    int i;
    char timestr[GF_TIMESTR_SIZE] = {
        0,
    };

    gf_time_fmt(timestr, sizeof(timestr), gf_time(), gf_timefmt_F_HMS);

    /* removing white spaces in timestamp */
    for (i = 0; i < strlen(timestr); i++) {
        if (timestr[i] == ' ')
            timestr[i] = '_';
    }
    strncat(name, "_", name_size - strlen(name) - 1);
    strncat(name, timestr, name_size - strlen(name) - 1);
}

/* *
 * Check whether delete/rename operation is permitted on
 * trash directory
 */

gf_boolean_t
check_whether_op_permitted(trash_private_t *priv, loc_t *loc)
{
    if ((priv->state && (gf_uuid_compare(loc->inode->gfid, trash_gfid) == 0)))
        return _gf_false;
    if (priv->internal &&
        (gf_uuid_compare(loc->inode->gfid, internal_op_gfid) == 0))
        return _gf_false;

    return _gf_true;
}

/**
 * Wipe the memory used by trash location variable
 */
void
trash_local_wipe(trash_local_t *local)
{
    if (!local)
        goto out;

    loc_wipe(&local->loc);
    loc_wipe(&local->newloc);

    if (local->fd)
        fd_unref(local->fd);
    if (local->newfd)
        fd_unref(local->newfd);

    mem_put(local);
out:
    return;
}

/**
 * Wipe the memory used by eliminate path through a
 * recursive call
 */
void
wipe_eliminate_path(trash_elim_path **trav)
{
    if (trav == NULL) {
        return;
    }

    if (*trav == NULL) {
        return;
    }

    wipe_eliminate_path(&(*trav)->next);
    GF_FREE((*trav)->path);
    GF_FREE(*trav);
    *trav = NULL;
}

/**
 * This is the call back of rename fop initated using STACK_WIND in
 * reconfigure/notify function which is used to rename trash directory
 * in the brick when it is required either in volume start or set.
 * This frame  must destroyed from this function itself since it was
 * created by trash xlator
 */
int32_t
trash_dir_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, struct iatt *buf,
                     struct iatt *preoldparent, struct iatt *postoldparent,
                     struct iatt *prenewparent, struct iatt *postnewparent,
                     dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL;

    priv = this->private;

    local = frame->local;

    if (op_ret == -1) {
        gf_log(this->name, GF_LOG_ERROR,
               "rename trash directory "
               "failed: %s",
               strerror(op_errno));
        goto out;
    }

    GF_FREE(priv->oldtrash_dir);

    priv->oldtrash_dir = gf_strdup(priv->newtrash_dir);
    if (!priv->oldtrash_dir) {
        op_ret = ENOMEM;
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
    }

out:
    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return op_ret;
}

int
rename_trash_directory(xlator_t *this)
{
    trash_private_t *priv = NULL;
    int ret = 0;
    loc_t loc = {
        0,
    };
    loc_t old_loc = {
        0,
    };
    call_frame_t *frame = NULL;
    trash_local_t *local = NULL;

    priv = this->private;

    frame = create_frame(this, this->ctx->pool);
    if (frame == NULL) {
        gf_log(this->name, GF_LOG_ERROR, "failed to create frame");
        ret = ENOMEM;
        goto out;
    }

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    frame->local = local;

    /* assign new location values to new_loc members */
    gf_uuid_copy(loc.gfid, trash_gfid);
    gf_uuid_copy(loc.pargfid, root_gfid);
    ret = extract_trash_directory(priv->newtrash_dir, &loc.name);
    if (ret) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }
    loc.path = gf_strdup(priv->newtrash_dir);
    if (!loc.path) {
        ret = ENOMEM;
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }

    /* assign old location values to old_loc members */
    gf_uuid_copy(old_loc.gfid, trash_gfid);
    gf_uuid_copy(old_loc.pargfid, root_gfid);
    ret = extract_trash_directory(priv->oldtrash_dir, &old_loc.name);
    if (ret) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }
    old_loc.path = gf_strdup(priv->oldtrash_dir);
    if (!old_loc.path) {
        ret = ENOMEM;
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }

    old_loc.inode = inode_ref(priv->trash_inode);
    gf_uuid_copy(old_loc.inode->gfid, old_loc.gfid);

    loc_copy(&local->loc, &old_loc);
    loc_copy(&local->newloc, &loc);

    STACK_WIND(frame, trash_dir_rename_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->rename, &old_loc, &loc, NULL);
    return 0;

out:
    if (frame) {
        frame->local = NULL;
        STACK_DESTROY(frame->root);
    }

    trash_local_wipe(local);

    return ret;
}

int32_t
trash_internal_op_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                            int32_t op_ret, int32_t op_errno, inode_t *inode,
                            struct iatt *buf, struct iatt *preparent,
                            struct iatt *postparent, dict_t *xdata)
{
    trash_local_t *local = NULL;
    local = frame->local;

    if (op_ret != 0 && !(op_errno == EEXIST))
        gf_log(this->name, GF_LOG_ERROR,
               "mkdir failed for "
               "internal op directory : %s",
               strerror(op_errno));

    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return op_ret;
}

/**
 * This is the call back of mkdir fop initated using STACK_WIND in
 * notify/reconfigure function which is used to create trash directory
 * in the brick when "trash" is on. The frame of the mkdir must
 * destroyed from this function itself since it was created by trash xlator
 */

int32_t
trash_dir_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, inode_t *inode,
                    struct iatt *buf, struct iatt *preparent,
                    struct iatt *postparent, dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL;

    priv = this->private;

    local = frame->local;

    if (op_ret == 0) {
        priv->oldtrash_dir = gf_strdup(priv->newtrash_dir);
        if (!priv->oldtrash_dir) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            op_ret = ENOMEM;
        }
    } else if (op_ret != 0 && errno != EEXIST)
        gf_log(this->name, GF_LOG_ERROR,
               "mkdir failed for trash"
               " directory : %s",
               strerror(op_errno));

    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return op_ret;
}

/**
 * This getxattr calls returns existing trash directory path in
 * the dictionary
 */
int32_t
trash_dir_getxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                       int32_t op_ret, int32_t op_errno, dict_t *dict,
                       dict_t *xdata)
{
    data_t *data = NULL;
    trash_private_t *priv = NULL;
    int ret = 0;
    trash_local_t *local = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;

    data = dict_get(dict, GET_ANCESTRY_PATH_KEY);
    if (!data) {
        goto out;
    }
    priv->oldtrash_dir = GF_MALLOC(PATH_MAX, gf_common_mt_char);
    if (!priv->oldtrash_dir) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    /* appending '/' if it is not present */
    sprintf(priv->oldtrash_dir, "%s%c", data->data,
            data->data[strlen(data->data) - 1] != '/' ? '/' : '\0');
    gf_log(this->name, GF_LOG_DEBUG,
           "old trash directory path "
           "is %s",
           priv->oldtrash_dir);
    if (strcmp(priv->newtrash_dir, priv->oldtrash_dir) != 0) {
        /* When user set a new name for trash directory, trash
         * xlator will perform a rename operation on old trash
         * directory to the new one using a STACK_WIND from here.
         * This option can be configured only when volume is in
         * started state
         */
        ret = rename_trash_directory(this);
    }

out:
    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return ret;
}
/**
 * This is a nameless look up for internal op directory
 * The lookup is based on gfid, because internal op directory
 * has fixed gfid.
 */
int32_t
trash_internalop_dir_lookup_cbk(call_frame_t *frame, void *cookie,
                                xlator_t *this, int32_t op_ret,
                                int32_t op_errno, inode_t *inode,
                                struct iatt *buf, dict_t *xdata,
                                struct iatt *postparent)
{
    trash_private_t *priv = NULL;
    int ret = 0;
    uuid_t *gfid_ptr = NULL;
    loc_t loc = {
        0,
    };
    char internal_op_path[PATH_MAX] = {
        0,
    };
    dict_t *dict = NULL;
    trash_local_t *local = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    if (op_ret != 0 && op_errno == ENOENT) {
        loc_wipe(&local->loc);
        gfid_ptr = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t);
        if (!gfid_ptr) {
            ret = ENOMEM;
            goto out;
        }

        gf_uuid_copy(*gfid_ptr, internal_op_gfid);

        dict = dict_new();
        if (!dict) {
            ret = ENOMEM;
            goto out;
        }
        ret = dict_set_gfuuid(dict, "gfid-req", *gfid_ptr, false);
        if (ret) {
            gf_log(this->name, GF_LOG_ERROR, "setting key gfid-req failed");
            goto out;
        }
        gf_uuid_copy(loc.gfid, internal_op_gfid);
        gf_uuid_copy(loc.pargfid, trash_gfid);

        loc.inode = inode_new(priv->trash_itable);

        /* The mkdir call for creating internal op directory */
        loc.name = gf_strdup("internal_op");
        if (!loc.name) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        sprintf(internal_op_path, "%s%s/", priv->newtrash_dir, loc.name);

        loc.path = gf_strdup(internal_op_path);
        if (!loc.path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }

        loc_copy(&local->loc, &loc);
        STACK_WIND(frame, trash_internal_op_mkdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->mkdir, &loc, 0755, 0022, dict);
        return 0;
    }

out:
    if (ret && gfid_ptr)
        GF_FREE(gfid_ptr);
    if (dict)
        dict_unref(dict);
    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return op_ret;
}

/**
 * This is a nameless look up for old trash directory
 * The lookup is based on gfid, because trash directory
 * has fixed gfid.
 */
int32_t
trash_dir_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, inode_t *inode,
                     struct iatt *buf, dict_t *xdata, struct iatt *postparent)
{
    trash_private_t *priv = NULL;
    loc_t loc = {
        0,
    };
    int ret = 0;
    uuid_t *gfid_ptr = NULL;
    dict_t *dict = NULL;
    trash_local_t *local = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;

    loc_wipe(&local->loc);
    if (op_ret == 0) {
        gf_log(this->name, GF_LOG_DEBUG, "inode found with gfid %s",
               uuid_utoa(buf->ia_gfid));

        gf_uuid_copy(loc.gfid, trash_gfid);

        /* Find trash inode using available information */
        priv->trash_inode = inode_link(inode, NULL, NULL, buf);

        loc.inode = inode_ref(priv->trash_inode);
        loc_copy(&local->loc, &loc);

        /*Used to find path of old trash directory*/
        STACK_WIND(frame, trash_dir_getxattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->getxattr, &loc,
                   GET_ANCESTRY_PATH_KEY, xdata);
        return 0;
    }

    /* If there is no old trash directory we set its value to new one,
     * which is the valid condition for trash directory creation
     */
    else {
        gf_log(this->name, GF_LOG_DEBUG,
               "Creating trash "
               "directory %s ",
               priv->newtrash_dir);

        gfid_ptr = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t);
        if (!gfid_ptr) {
            ret = ENOMEM;
            goto out;
        }
        gf_uuid_copy(*gfid_ptr, trash_gfid);

        gf_uuid_copy(loc.gfid, trash_gfid);
        gf_uuid_copy(loc.pargfid, root_gfid);
        ret = extract_trash_directory(priv->newtrash_dir, &loc.name);
        if (ret) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
        loc.path = gf_strdup(priv->newtrash_dir);
        if (!loc.path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }

        priv->trash_inode = inode_new(priv->trash_itable);
        priv->trash_inode->ia_type = IA_IFDIR;
        loc.inode = inode_ref(priv->trash_inode);
        dict = dict_new();
        if (!dict) {
            ret = ENOMEM;
            goto out;
        }
        /* Fixed gfid is set for trash directory with
         * this function
         */
        ret = dict_set_gfuuid(dict, "gfid-req", *gfid_ptr, false);
        if (ret) {
            gf_log(this->name, GF_LOG_ERROR, "setting key gfid-req failed");
            goto out;
        }
        loc_copy(&local->loc, &loc);

        /* The mkdir call for creating trash directory */
        STACK_WIND(frame, trash_dir_mkdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->mkdir, &loc, 0755, 0022, dict);
        return 0;
    }
out:
    if (ret && gfid_ptr)
        GF_FREE(gfid_ptr);
    if (dict)
        dict_unref(dict);
    frame->local = NULL;
    STACK_DESTROY(frame->root);
    trash_local_wipe(local);
    return ret;
}

int
create_or_rename_trash_directory(xlator_t *this)
{
    trash_private_t *priv = NULL;
    int ret = 0;
    loc_t loc = {
        0,
    };
    call_frame_t *frame = NULL;
    trash_local_t *local = NULL;

    priv = this->private;

    frame = create_frame(this, this->ctx->pool);
    if (frame == NULL) {
        gf_log(this->name, GF_LOG_ERROR, "failed to create frame");
        ret = ENOMEM;
        goto out;
    }

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    frame->local = local;

    loc.inode = inode_new(priv->trash_itable);
    gf_uuid_copy(loc.gfid, trash_gfid);
    loc_copy(&local->loc, &loc);
    gf_log(this->name, GF_LOG_DEBUG,
           "nameless lookup for"
           "old trash directory");
    STACK_WIND(frame, trash_dir_lookup_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->lookup, &loc, NULL);
out:
    return ret;
}

int
create_internalop_directory(xlator_t *this)
{
    trash_private_t *priv = NULL;
    int ret = 0;
    loc_t loc = {
        0,
    };
    call_frame_t *frame = NULL;
    trash_local_t *local = NULL;

    priv = this->private;

    frame = create_frame(this, this->ctx->pool);
    if (frame == NULL) {
        gf_log(this->name, GF_LOG_ERROR, "failed to create frame");
        ret = ENOMEM;
        goto out;
    }

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    frame->local = local;

    gf_uuid_copy(loc.gfid, internal_op_gfid);
    gf_uuid_copy(loc.pargfid, trash_gfid);
    loc.inode = inode_new(priv->trash_itable);
    loc.inode->ia_type = IA_IFDIR;

    loc_copy(&local->loc, &loc);
    STACK_WIND(frame, trash_internalop_dir_lookup_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->lookup, &loc, NULL);
out:

    return ret;
}

int32_t
trash_common_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                       int32_t op_ret, int32_t op_errno, inode_t *inode,
                       struct iatt *buf, struct iatt *preparent,
                       struct iatt *postparent, dict_t *xdata)
{
    STACK_UNWIND_STRICT(mkdir, frame, op_ret, op_errno, inode, buf, preparent,
                        postparent, xdata);
    return 0;
}

int32_t
trash_common_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, struct iatt *buf,
                        struct iatt *preoldparent, struct iatt *postoldparent,
                        struct iatt *prenewparent, struct iatt *postnewparent,
                        dict_t *xdata)
{
    STACK_UNWIND_STRICT(rename, frame, op_ret, op_errno, buf, preoldparent,
                        postoldparent, prenewparent, postnewparent, xdata);
    return 0;
}

int32_t
trash_common_rmdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                       int32_t op_ret, int32_t op_errno, struct iatt *preparent,
                       struct iatt *postparent, dict_t *xdata)
{
    STACK_UNWIND_STRICT(rmdir, frame, op_ret, op_errno, preparent, postparent,
                        xdata);
    return 0;
}

/**
 * move backs from trash translator to unlink call
 */
int32_t
trash_common_unwind_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno,
                        struct iatt *preparent, struct iatt *postparent,
                        dict_t *xdata)
{
    TRASH_STACK_UNWIND(unlink, frame, op_ret, op_errno, preparent, postparent,
                       xdata);
    return 0;
}

/**
 * If the path is not present in the trash directory,it will recursively
 * call this call-back and one by one directories will be created from
 * the starting
 */
int32_t
trash_unlink_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                       int32_t op_ret, int32_t op_errno, inode_t *inode,
                       struct iatt *stbuf, struct iatt *preparent,
                       struct iatt *postparent, dict_t *xdata)
{
    trash_local_t *local = NULL;
    char *tmp_str = NULL;
    char *tmp_path = NULL;
    char *tmp_dirname = NULL;
    char *tmp_stat = NULL;
    char real_path[PATH_MAX] = {
        0,
    };
    char *dir_name = NULL;
    size_t count = 0;
    int32_t loop_count = 0;
    int i = 0;
    loc_t tmp_loc = {
        0,
    };
    trash_private_t *priv = NULL;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    TRASH_UNSET_PID(frame, local);

    tmp_str = gf_strdup(local->newpath);
    if (!tmp_str) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = -1;
        goto out;
    }
    loop_count = local->loop_count;

    /* The directory is not present , need to create it */
    if ((op_ret == -1) && (op_errno == ENOENT)) {
        tmp_dirname = strchr(tmp_str, '/');
        while (tmp_dirname) {
            count = tmp_dirname - tmp_str;
            if (count == 0)
                count = 1;
            i++;
            if (i > loop_count)
                break;
            tmp_dirname = strchr(tmp_str + count + 1, '/');
        }
        tmp_path = gf_memdup(local->newpath, count + 1);
        if (!tmp_path) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        tmp_path[count] = '\0';

        loc_copy(&tmp_loc, &local->loc);
        tmp_loc.path = gf_strdup(tmp_path);
        if (!tmp_loc.path) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            ret = ENOMEM;
            goto out;
        }

        /* Stores the the name of directory to be created */
        tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1);
        if (!tmp_loc.name) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        strncpy(real_path, priv->brick_path, sizeof(real_path));
        real_path[sizeof(real_path) - 1] = 0;

        remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat);
        if (tmp_stat)
            strncat(real_path, tmp_stat,
                    sizeof(real_path) - strlen(real_path) - 1);

        TRASH_SET_PID(frame, local);

        STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_path,
                          FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                          &tmp_loc, get_permission(real_path), 0022, xdata);
        loc_wipe(&tmp_loc);
        goto out;
    }

    /* Given path is created , comparing to the required path */
    if (op_ret == 0) {
        dir_name = dirname(tmp_str);
        if (strcmp((char *)cookie, dir_name) == 0) {
            /* File path exists we can rename it*/
            loc_copy(&tmp_loc, &local->loc);
            tmp_loc.path = local->newpath;
            STACK_WIND(frame, trash_unlink_rename_cbk, FIRST_CHILD(this),
                       FIRST_CHILD(this)->fops->rename, &local->loc, &tmp_loc,
                       xdata);
            goto out;
        }
    }

    if ((op_ret == -1) && (op_errno != EEXIST)) {
        gf_log(this->name, GF_LOG_ERROR,
               "Directory creation failed [%s]. "
               "Therefore unlinking %s without moving to trash "
               "directory",
               strerror(op_errno), local->loc.name);
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata);
        goto out;
    }

    LOCK(&frame->lock);
    {
        loop_count = ++local->loop_count;
    }
    UNLOCK(&frame->lock);

    tmp_dirname = strchr(tmp_str, '/');

    /* Path is not completed , need to create remaining path */
    while (tmp_dirname) {
        count = tmp_dirname - tmp_str;
        if (count == 0)
            count = 1;
        i++;
        if (i > loop_count)
            break;
        tmp_dirname = strchr(tmp_str + count + 1, '/');
    }
    tmp_path = gf_memdup(local->newpath, count + 1);
    if (!tmp_path) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = -1;
        goto out;
    }
    tmp_path[count] = '\0';

    loc_copy(&tmp_loc, &local->loc);
    tmp_loc.path = gf_strdup(tmp_path);
    if (!tmp_loc.path) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = -1;
        goto out;
    }

    /* Stores the the name of directory to be created */
    tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1);
    if (!tmp_loc.name) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = -1;
        goto out;
    }

    strncpy(real_path, priv->brick_path, sizeof(real_path));
    real_path[sizeof(real_path) - 1] = 0;

    remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat);
    if (tmp_stat)
        strncat(real_path, tmp_stat, sizeof(real_path) - strlen(real_path) - 1);

    TRASH_SET_PID(frame, local);

    STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_path,
                      FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                      &tmp_loc, get_permission(real_path), 0022, xdata);

out:
    if (tmp_path)
        GF_FREE(tmp_path);
    if (tmp_str)
        GF_FREE(tmp_str);
    return ret;
}

/**
 * The name of unlinking file should be renamed as starting
 * from trash directory as mentioned in the mount point
 */
int32_t
trash_unlink_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, struct iatt *buf,
                        struct iatt *preoldparent, struct iatt *postoldparent,
                        struct iatt *prenewparent, struct iatt *postnewparent,
                        dict_t *xdata)
{
    trash_local_t *local = NULL;
    trash_private_t *priv = NULL;
    char *tmp_str = NULL;
    char *dir_name = NULL;
    char *tmp_cookie = NULL;
    loc_t tmp_loc = {
        0,
    };
    dict_t *new_xdata = NULL;
    char *tmp_stat = NULL;
    char real_path[PATH_MAX] = {
        0,
    };
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if ((op_ret == -1) && (op_errno == ENOENT)) {
        /* the file path does not exist we want to create path
         * for the file
         */
        tmp_str = gf_strdup(local->newpath);
        if (!tmp_str) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        dir_name = dirname(tmp_str); /* stores directory name */

        loc_copy(&tmp_loc, &local->loc);
        tmp_loc.path = gf_strdup(dir_name);
        if (!tmp_loc.path) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            ret = ENOMEM;
            goto out;
        }

        tmp_cookie = gf_strdup(dir_name);
        if (!tmp_cookie) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        strncpy(real_path, priv->brick_path, sizeof(real_path));
        real_path[sizeof(real_path) - 1] = 0;
        remove_trash_path(tmp_str, (frame->root->pid < 0), &tmp_stat);
        if (tmp_stat)
            strncat(real_path, tmp_stat,
                    sizeof(real_path) - strlen(real_path) - 1);

        TRASH_SET_PID(frame, local);

        /* create the directory with proper permissions */
        STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_cookie,
                          FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                          &tmp_loc, get_permission(real_path), 0022, xdata);
        loc_wipe(&tmp_loc);
        goto out;
    }

    if ((op_ret == -1) && (op_errno == ENOTDIR)) {
        /* if entry is already present in trash directory,
         * new one is not copied*/
        gf_log(this->name, GF_LOG_DEBUG,
               "target(%s) exists, cannot keep the copy, deleting",
               local->newpath);

        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata);

        goto out;
    }

    if ((op_ret == -1) && (op_errno == EISDIR)) {
        /* if entry is directory,we remove directly */
        gf_log(this->name, GF_LOG_DEBUG,
               "target(%s) exists as directory, cannot keep copy, "
               "deleting",
               local->newpath);

        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata);
        goto out;
    }

    /**********************************************************************
     *
     * CTR Xlator message handling done here!
     *
     **********************************************************************/
    /**
     * If unlink is handled by trash translator, it should inform the
     * CTR Xlator. And trash translator only handles the unlink for
     * the last hardlink.
     *
     * Check if there is a GF_REQUEST_LINK_COUNT_XDATA from CTR Xlator
     *
     */

    if (local->ctr_link_count_req) {
        /* Sending back inode link count to ctr_unlink
         * (changetimerecoder xlator) via
         * "GF_RESPONSE_LINK_COUNT_XDATA" key using xdata.
         * */
        if (xdata) {
            ret = dict_set_uint32(xdata, GF_RESPONSE_LINK_COUNT_XDATA, 1);
            if (ret == -1) {
                gf_log(this->name, GF_LOG_WARNING,
                       "Failed to set"
                       " GF_RESPONSE_LINK_COUNT_XDATA");
            }
        } else {
            new_xdata = dict_new();
            if (!new_xdata) {
                gf_log(this->name, GF_LOG_WARNING,
                       "Memory allocation failure while "
                       "creating new_xdata");
                goto ctr_out;
            }
            ret = dict_set_uint32(new_xdata, GF_RESPONSE_LINK_COUNT_XDATA, 1);
            if (ret == -1) {
                gf_log(this->name, GF_LOG_WARNING,
                       "Failed to set"
                       " GF_RESPONSE_LINK_COUNT_XDATA");
            }
        ctr_out:
            TRASH_STACK_UNWIND(unlink, frame, 0, op_errno, preoldparent,
                               postoldparent, new_xdata);
            goto out;
        }
    }
    /* All other cases, unlink should return success */
    TRASH_STACK_UNWIND(unlink, frame, 0, op_errno, preoldparent, postoldparent,
                       xdata);
out:

    if (tmp_str)
        GF_FREE(tmp_str);
    if (tmp_cookie)
        GF_FREE(tmp_cookie);
    if (new_xdata)
        dict_unref(new_xdata);

    return ret;
}

/**
 * move backs from trash translator to truncate call
 */
int32_t
trash_common_unwind_buf_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                            int32_t op_ret, int32_t op_errno,
                            struct iatt *prebuf, struct iatt *postbuf,
                            dict_t *xdata)
{
    TRASH_STACK_UNWIND(truncate, frame, op_ret, op_errno, prebuf, postbuf,
                       xdata);
    return 0;
}

int32_t
trash_unlink_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, struct iatt *buf,
                      dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL;
    loc_t new_loc = {
        0,
    };
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if (op_ret == -1) {
        gf_log(this->name, GF_LOG_DEBUG, "%s: %s", local->loc.path,
               strerror(op_errno));
        TRASH_STACK_UNWIND(unlink, frame, op_ret, op_errno, buf, NULL, xdata);
        ret = -1;
        goto out;
    }

    /* Only last hardlink will be moved to trash directory */
    if (buf->ia_nlink > 1) {
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata);
        goto out;
    }

    /* if the file is too big  just unlink it */
    if (buf->ia_size > (priv->max_trash_file_size)) {
        gf_log(this->name, GF_LOG_DEBUG,
               "%s: file size too big (%" PRId64
               ") to "
               "move into trash directory",
               local->loc.path, buf->ia_size);

        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata);
        goto out;
    }

    /* Copies new path for renaming */
    loc_copy(&new_loc, &local->loc);
    new_loc.path = gf_strdup(local->newpath);
    if (!new_loc.path) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }

    STACK_WIND(frame, trash_unlink_rename_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->rename, &local->loc, &new_loc, xdata);

out:
    loc_wipe(&new_loc);

    return ret;
}

/**
 * Unlink is called internally by rm system call and also
 * by internal operations of gluster such as self-heal
 */
int32_t
trash_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflags,
             dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL; /* files inside trash */
    int32_t match = 0;
    int32_t ctr_link_req = 0;
    char *pathbuf = NULL;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    /* If trash is not active or not enabled through cli, then
     *  we bypass and wind back
     */
    if (!priv->state) {
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, 0, xdata);
        goto out;
    }

    /* The files removed by gluster internal operations such as self-heal,
     * should moved to trash directory , but files by client should not
     * moved
     */
    if ((frame->root->pid < 0) && !priv->internal) {
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, 0, xdata);
        goto out;
    }
    /* loc need some gfid which will be present in inode */
    gf_uuid_copy(loc->gfid, loc->inode->gfid);

    /* Checking for valid location */
    if (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) {
        gf_log(this->name, GF_LOG_DEBUG, "Bad address");
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, 0, xdata);
        ret = EFAULT;
        goto out;
    }

    /* This will be more accurate */
    inode_path(loc->inode, NULL, &pathbuf);
    /* Check whether the file is present under eliminate paths or
     * inside trash directory. In both cases we don't need to move the
     * file to trash directory. Instead delete it permanently
     */
    match = check_whether_eliminate_path(priv->eliminate, pathbuf);
    if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) ==
         0) ||
        (match)) {
        if (match) {
            gf_log(this->name, GF_LOG_DEBUG,
                   "%s is a file comes under an eliminate path, "
                   "so it is not moved to trash",
                   loc->name);
        }

        /* Trying to unlink from the trash-dir. So do the
         * actual unlink without moving to trash-dir.
         */
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, 0, xdata);
        goto out;
    }

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        TRASH_STACK_UNWIND(unlink, frame, -1, ENOMEM, NULL, NULL, xdata);
        ret = ENOMEM;
        goto out;
    }
    frame->local = local;
    loc_copy(&local->loc, loc);

    /* rename new location of file as starting from trash directory */
    copy_trash_path(priv->newtrash_dir, (frame->root->pid < 0), local->newpath,
                    sizeof(local->newpath));
    strncat(local->newpath, pathbuf,
            sizeof(local->newpath) - strlen(local->newpath) - 1);

    /* append timestamp to file name so that we can avoid
     * name collisions inside trash
     */
    append_time_stamp(local->newpath, sizeof(local->newpath));
    if (strlen(local->newpath) > PATH_MAX) {
        STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, 0, xdata);
        goto out;
    }

    /* To know whether CTR xlator requested for the link count */
    ret = dict_get_int32(xdata, GF_REQUEST_LINK_COUNT_XDATA, &ctr_link_req);
    if (ret) {
        local->ctr_link_count_req = _gf_false;
        ret = 0;
    } else
        local->ctr_link_count_req = _gf_true;

    LOCK_INIT(&frame->lock);

    STACK_WIND(frame, trash_unlink_stat_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->stat, loc, xdata);
out:
    return ret;
}

/**
 * Use this when a failure occurs, and delete the newly created file
 */
int32_t
trash_truncate_unlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                          int32_t op_ret, int32_t op_errno,
                          struct iatt *preparent, struct iatt *postparent,
                          dict_t *xdata)
{
    trash_local_t *local = NULL;

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if (op_ret == -1) {
        gf_log(this->name, GF_LOG_DEBUG, "deleting the newly created file: %s",
               strerror(op_errno));
    }

    STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->truncate, &local->loc,
               local->fop_offset, xdata);
out:
    return 0;
}

/**
 * Read from source file
 */
int32_t
trash_truncate_readv_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                         int32_t op_ret, int32_t op_errno, struct iovec *vector,
                         int32_t count, struct iatt *stbuf,
                         struct iobref *iobuf, dict_t *xdata)
{
    trash_local_t *local = NULL;

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if (op_ret == -1) {
        gf_log(this->name, GF_LOG_DEBUG,
               "readv on the existing file failed: %s", strerror(op_errno));

        STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata);
        goto out;
    }

    local->fsize = stbuf->ia_size;
    STACK_WIND(frame, trash_truncate_writev_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->writev, local->newfd, vector, count,
               local->cur_offset, 0, iobuf, xdata);

out:
    return 0;
}

/**
 * Write to file created in trash directory
 */
int32_t
trash_truncate_writev_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                          int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
                          struct iatt *postbuf, dict_t *xdata)
{
    trash_local_t *local = NULL;

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if (op_ret == -1) {
        /* Let truncate work, but previous copy is not preserved. */
        gf_log(this->name, GF_LOG_DEBUG,
               "writev on the existing file failed: %s", strerror(op_errno));

        STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata);
        goto out;
    }

    if (local->cur_offset < local->fsize) {
        local->cur_offset += GF_BLOCK_READV_SIZE;
        /* Loop back and Read the contents again. */
        STACK_WIND(frame, trash_truncate_readv_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->readv, local->fd,
                   (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, 0, xdata);
        goto out;
    }

    /* OOFH.....Finally calling Truncate. */
    STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->truncate, &local->loc,
               local->fop_offset, xdata);

out:
    return 0;
}

/**
 * The source file is opened for reading and writing
 */
int32_t
trash_truncate_open_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, fd_t *fd,
                        dict_t *xdata)
{
    trash_local_t *local = NULL;

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    if (op_ret == -1) {
        /* Let truncate work, but previous copy is not preserved. */
        gf_log(this->name, GF_LOG_DEBUG, "open on the existing file failed: %s",
               strerror(op_errno));

        STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata);
        goto out;
    }

    fd_bind(fd);

    local->cur_offset = 0;

    STACK_WIND(frame, trash_truncate_readv_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->readv, local->fd,
               (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, 0, xdata);

out:
    return 0;
}

/**
 * Creates new file descriptor for read and write operations,
 * if the path is present in trash directory
 */
int32_t
trash_truncate_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                          int32_t op_ret, int32_t op_errno, fd_t *fd,
                          inode_t *inode, struct iatt *buf,
                          struct iatt *preparent, struct iatt *postparent,
                          dict_t *xdata)
{
    trash_local_t *local = NULL;
    char *tmp_str = NULL;
    char *dir_name = NULL;
    char *tmp_path = NULL;
    int32_t flags = 0;
    loc_t tmp_loc = {
        0,
    };
    char *tmp_stat = NULL;
    char real_path[PATH_MAX] = {
        0,
    };
    trash_private_t *priv = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    TRASH_UNSET_PID(frame, local);

    /* Checks whether path is present in trash directory or not */

    if ((op_ret == -1) && (op_errno == ENOENT)) {
        /* Creating the directory structure here. */
        tmp_str = gf_strdup(local->newpath);
        if (!tmp_str) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
        dir_name = dirname(tmp_str);

        tmp_path = gf_strdup(dir_name);
        if (!tmp_path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
        loc_copy(&tmp_loc, &local->newloc);
        tmp_loc.path = gf_strdup(tmp_path);
        if (!tmp_loc.path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
        strncpy(real_path, priv->brick_path, sizeof(real_path));
        real_path[sizeof(real_path) - 1] = 0;
        remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat);
        if (tmp_stat)
            strncat(real_path, tmp_stat,
                    sizeof(real_path) - strlen(real_path) - 1);

        TRASH_SET_PID(frame, local);

        /* create the directory with proper permissions */
        STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path,
                          FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                          &tmp_loc, get_permission(real_path), 0022, xdata);
        loc_wipe(&tmp_loc);
        goto out;
    }

    if (op_ret == -1) {
        /* Let truncate work, but previous copy is not preserved.
         * Deleting the newly created copy.
         */
        gf_log(this->name, GF_LOG_DEBUG,
               "creation of new file in trash-dir failed, "
               "when truncate was called: %s",
               strerror(op_errno));

        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, &local->loc,
                   local->fop_offset, xdata);
        goto out;
    }

    fd_bind(fd);
    flags = O_RDONLY;

    /* fd which represents source file for reading and writing from it */

    local->fd = fd_create(local->loc.inode, frame->root->pid);

    STACK_WIND(frame, trash_truncate_open_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->open, &local->loc, flags, local->fd, 0);
out:
    if (tmp_str)
        GF_FREE(tmp_str);
    if (tmp_path)
        GF_FREE(tmp_path);

    return 0;
}

/**
 * If the path is not present in the trash directory,it will recursively call
 * this call-back and one by one directories will be created from the
 * beginning
 */
int32_t
trash_truncate_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                         int32_t op_ret, int32_t op_errno, inode_t *inode,
                         struct iatt *stbuf, struct iatt *preparent,
                         struct iatt *postparent, dict_t *xdata)
{
    trash_local_t *local = NULL;
    trash_private_t *priv = NULL;
    char *tmp_str = NULL;
    char *tmp_path = NULL;
    char *tmp_dirname = NULL;
    char *dir_name = NULL;
    char *tmp_stat = NULL;
    char real_path[PATH_MAX] = {
        0,
    };
    size_t count = 0;
    int32_t flags = 0;
    int32_t loop_count = 0;
    int i = 0;
    loc_t tmp_loc = {
        0,
    };
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    loop_count = local->loop_count;

    TRASH_UNSET_PID(frame, local);

    tmp_str = gf_strdup(local->newpath);
    if (!tmp_str) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }

    if ((op_ret == -1) && (op_errno == ENOENT)) {
        tmp_dirname = strchr(tmp_str, '/');
        while (tmp_dirname) {
            count = tmp_dirname - tmp_str;
            if (count == 0)
                count = 1;
            i++;
            if (i > loop_count)
                break;
            tmp_dirname = strchr(tmp_str + count + 1, '/');
        }
        tmp_path = gf_memdup(local->newpath, count + 1);
        if (!tmp_path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        tmp_path[count] = '\0';

        loc_copy(&tmp_loc, &local->newloc);
        tmp_loc.path = gf_strdup(tmp_path);
        if (!tmp_loc.path) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }

        /* Stores the the name of directory to be created */
        tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1);
        if (!tmp_loc.name) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        strncpy(real_path, priv->brick_path, sizeof(real_path));
        real_path[sizeof(real_path) - 1] = 0;
        remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat);
        if (tmp_stat)
            strncat(real_path, tmp_stat,
                    sizeof(real_path) - strlen(real_path) - 1);

        TRASH_SET_PID(frame, local);

        STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path,
                          FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                          &tmp_loc, get_permission(real_path), 0022, xdata);
        loc_wipe(&tmp_loc);
        goto out;
    }

    if (op_ret == 0) {
        dir_name = dirname(tmp_str);
        if (strcmp((char *)cookie, dir_name) == 0) {
            flags = O_CREAT | O_EXCL | O_WRONLY;
            strncpy(real_path, priv->brick_path, sizeof(real_path));
            real_path[sizeof(real_path) - 1] = 0;
            strncat(real_path, local->origpath,
                    sizeof(real_path) - strlen(real_path) - 1);
            /* Call create again once directory structure
               is created. */

            TRASH_SET_PID(frame, local);

            STACK_WIND(frame, trash_truncate_create_cbk, FIRST_CHILD(this),
                       FIRST_CHILD(this)->fops->create, &local->newloc, flags,
                       get_permission(real_path), 0022, local->newfd, xdata);
            goto out;
        }
    }

    if ((op_ret == -1) && (op_errno != EEXIST)) {
        gf_log(this->name, GF_LOG_ERROR,
               "Directory creation failed [%s]. "
               "Therefore truncating %s without moving the "
               "original copy to trash directory",
               strerror(op_errno), local->loc.name);
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, &local->loc,
                   local->fop_offset, xdata);
        goto out;
    }

    LOCK(&frame->lock);
    {
        loop_count = ++local->loop_count;
    }
    UNLOCK(&frame->lock);

    tmp_dirname = strchr(tmp_str, '/');
    while (tmp_dirname) {
        count = tmp_dirname - tmp_str;
        if (count == 0)
            count = 1;
        i++;
        if (i > loop_count)
            break;
        tmp_dirname = strchr(tmp_str + count + 1, '/');
    }
    tmp_path = gf_memdup(local->newpath, count + 1);
    if (!tmp_path) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    tmp_path[count] = '\0';

    loc_copy(&tmp_loc, &local->newloc);
    tmp_loc.path = gf_strdup(tmp_path);
    if (!tmp_loc.path) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }

    /* Stores the the name of directory to be created */
    tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1);
    if (!tmp_loc.name) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }

    strncpy(real_path, priv->brick_path, sizeof(real_path));
    real_path[sizeof(real_path) - 1] = 0;
    remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat);
    if (tmp_stat)
        strncat(real_path, tmp_stat, sizeof(real_path) - strlen(real_path) - 1);

    TRASH_SET_PID(frame, local);

    STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path,
                      FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
                      &tmp_loc, get_permission(real_path), 0022, xdata);

out:
    if (tmp_str)
        GF_FREE(tmp_str);
    if (tmp_path)
        GF_FREE(tmp_path);

    return ret;
}

int32_t
trash_truncate_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, struct iatt *buf,
                        dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL;
    char loc_newname[PATH_MAX] = {
        0,
    };
    int32_t flags = 0;
    dentry_t *dir_entry = NULL;
    inode_table_t *table = NULL;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    local = frame->local;
    GF_VALIDATE_OR_GOTO("trash", local, out);

    table = local->loc.inode->table;

    pthread_mutex_lock(&table->lock);
    {
        dir_entry = __dentry_search_arbit(local->loc.inode);
    }
    pthread_mutex_unlock(&table->lock);

    if (op_ret == -1) {
        gf_log(this->name, GF_LOG_DEBUG, "fstat on the file failed: %s",
               strerror(op_errno));

        TRASH_STACK_UNWIND(truncate, frame, op_ret, op_errno, buf, NULL, xdata);
        goto out;
    }

    /* Only last hardlink will be moved to trash directory */
    if (buf->ia_nlink > 1) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, &local->loc,
                   local->fop_offset, xdata);
        goto out;
    }

    /**
     * If the file is too big or if it is extended truncate,
     * just don't move it to trash directory.
     */
    if (buf->ia_size > (priv->max_trash_file_size) ||
        buf->ia_size <= local->fop_offset) {
        gf_log(this->name, GF_LOG_DEBUG,
               "%s: file is too large to move to trash", local->loc.path);

        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, &local->loc,
                   local->fop_offset, xdata);
        goto out;
    }

    /* Retrieves the name of file from path */
    local->loc.name = gf_strdup(strrchr(local->loc.path, '/'));
    if (!local->loc.name) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }

    /* Stores new path for source file */
    copy_trash_path(priv->newtrash_dir, (frame->root->pid < 0), local->newpath,
                    sizeof(local->newpath));
    strncat(local->newpath, local->loc.path,
            sizeof(local->newpath) - strlen(local->newpath) - 1);

    /* append timestamp to file name so that we can avoid
       name collisions inside trash */
    append_time_stamp(local->newpath, sizeof(local->newpath));
    if (strlen(local->newpath) > PATH_MAX) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, &local->loc,
                   local->fop_offset, xdata);
        goto out;
    }

    strncpy(loc_newname, local->loc.name, sizeof(loc_newname));
    loc_newname[sizeof(loc_newname) - 1] = 0;
    append_time_stamp(loc_newname, sizeof(loc_newname));
    /* local->newloc represents old file(file inside trash),
       where as local->loc represents truncated file. We need
       to create new inode and fd for new file*/
    local->newloc.name = gf_strdup(loc_newname);
    if (!local->newloc.name) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    local->newloc.path = gf_strdup(local->newpath);
    if (!local->newloc.path) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        ret = ENOMEM;
        goto out;
    }
    local->newloc.inode = inode_new(local->loc.inode->table);
    local->newfd = fd_create(local->newloc.inode, frame->root->pid);

    /* Creating valid parent and pargfids for both files */

    if (dir_entry == NULL) {
        ret = EINVAL;
        goto out;
    }
    local->loc.parent = inode_ref(dir_entry->parent);
    gf_uuid_copy(local->loc.pargfid, dir_entry->parent->gfid);

    local->newloc.parent = inode_ref(dir_entry->parent);
    gf_uuid_copy(local->newloc.pargfid, dir_entry->parent->gfid);

    flags = O_CREAT | O_EXCL | O_WRONLY;

    TRASH_SET_PID(frame, local);

    STACK_WIND(frame, trash_truncate_create_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->create, &local->newloc, flags,
               st_mode_from_ia(buf->ia_prot, local->loc.inode->ia_type), 0022,
               local->newfd, xdata);

out:
    return ret;
}

/**
 * Truncate can be explicitly called or implicitly by some other applications
 * like text editors etc..
 */
int32_t
trash_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
               dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL;
    int32_t match = 0;
    char *pathbuf = NULL;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);
    /* If trash is not active or not enabled through cli, then
     * we bypass and wind back
     */
    if (!priv->state) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, loc, offset, xdata);
        goto out;
    }

    /* The files removed by gluster operations such as self-heal,
       should moved to trash directory, but files by client should
       not moved */
    if ((frame->root->pid < 0) && !priv->internal) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, loc, offset, xdata);
        goto out;
    }
    /* This will be more accurate */
    inode_path(loc->inode, NULL, &pathbuf);

    /* Checks whether file is in trash directory or eliminate path.
     * In all such cases it does not move to trash directory,
     * truncate will be performed
     */
    match = check_whether_eliminate_path(priv->eliminate, pathbuf);

    if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) ==
         0) ||
        (match)) {
        if (match) {
            gf_log(this->name, GF_LOG_DEBUG,
                   "%s: file not moved to trash as per option "
                   "'eliminate path'",
                   loc->path);
        }

        /* Trying to truncate from the trash-dir. So do the
         * actual truncate without moving to trash-dir.
         */
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, loc, offset, xdata);
        goto out;
    }

    LOCK_INIT(&frame->lock);

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        TRASH_STACK_UNWIND(truncate, frame, -1, ENOMEM, NULL, NULL, xdata);
        ret = ENOMEM;
        goto out;
    }

    strncpy(local->origpath, pathbuf, sizeof(local->origpath));
    local->origpath[sizeof(local->origpath) - 1] = 0;

    loc_copy(&local->loc, loc);
    local->loc.path = pathbuf;
    local->fop_offset = offset;

    frame->local = local;

    STACK_WIND(frame, trash_truncate_stat_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->stat, loc, xdata);

out:
    return ret;
}

/**
 * When we call truncate from terminal it comes to ftruncate of trash-xlator.
 * Since truncate internally calls ftruncate and we receive fd of the file,
 * other than that it also called by Rebalance operation
 */
int32_t
trash_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
                dict_t *xdata)
{
    trash_private_t *priv = NULL;
    trash_local_t *local = NULL; /* file inside trash */
    char *pathbuf = NULL;        /* path of file from fd */
    int32_t retval = 0;
    int32_t match = 0;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);
    /* If trash is not active or not enabled through cli, then
     * we bypass and wind back
     */
    if (!priv->state) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata);
        goto out;
    }

    /* The files removed by gluster operations such as self-heal,
     * should moved to trash directory, but files by client
     * should not moved
     */
    if ((frame->root->pid < 0) && !priv->internal) {
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata);
        goto out;
    }
    /* This will be more accurate */
    retval = inode_path(fd->inode, NULL, &pathbuf);

    /* Checking  the eliminate path */

    /* Checks whether file is trash directory or eliminate path or
     * invalid fd. In all such cases it does not move to trash directory,
     * ftruncate will be performed
     */
    match = check_whether_eliminate_path(priv->eliminate, pathbuf);
    if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) ==
         0) ||
        match || !retval) {
        if (match) {
            gf_log(this->name, GF_LOG_DEBUG,
                   "%s: file matches eliminate path, "
                   "not moved to trash",
                   pathbuf);
        }

        /* Trying to ftruncate from the trash-dir. So do the
         * actual ftruncate without moving to trash-dir
         */
        STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata);
        goto out;
    }

    local = mem_get0(this->local_pool);
    if (!local) {
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        TRASH_STACK_UNWIND(ftruncate, frame, -1, ENOMEM, NULL, NULL, xdata);
        ret = -1;
        goto out;
    }

    strncpy(local->origpath, pathbuf, sizeof(local->origpath));
    local->origpath[sizeof(local->origpath) - 1] = 0;

    /* To convert fd to location */
    frame->local = local;

    local->loc.path = pathbuf;
    local->loc.inode = inode_ref(fd->inode);
    gf_uuid_copy(local->loc.gfid, local->loc.inode->gfid);

    local->fop_offset = offset;

    /* Else remains same to truncate code, so from here flow goes
     * to truncate_stat
     */
    STACK_WIND(frame, trash_truncate_stat_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->fstat, fd, xdata);
out:
    return ret;
}

/**
 * The mkdir call is intercepted to avoid creation of
 * trash directory in the mount by the user
 */
int32_t
trash_mkdir(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
            mode_t umask, dict_t *xdata)
{
    int32_t op_ret = 0;
    int32_t op_errno = 0;
    trash_private_t *priv = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    if (!check_whether_op_permitted(priv, loc)) {
        gf_log(this->name, GF_LOG_WARNING,
               "mkdir issued on %s, which is not permitted",
               priv->newtrash_dir);
        op_errno = EPERM;
        op_ret = -1;

        STACK_UNWIND_STRICT(mkdir, frame, op_ret, op_errno, NULL, NULL, NULL,
                            NULL, xdata);
    } else {
        STACK_WIND(frame, trash_common_mkdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->mkdir, loc, mode, umask, xdata);
    }

out:
    return 0;
}

/**
 * The rename call is intercepted to avoid renaming
 * of trash directory in the mount by the user
 */
int
trash_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
             dict_t *xdata)
{
    int32_t op_ret = 0;
    int32_t op_errno = 0;
    trash_private_t *priv = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    if (!check_whether_op_permitted(priv, oldloc)) {
        gf_log(this->name, GF_LOG_WARNING,
               "rename issued on %s, which is not permitted",
               priv->newtrash_dir);
        op_errno = EPERM;
        op_ret = -1;

        STACK_UNWIND_STRICT(rename, frame, op_ret, op_errno, NULL, NULL, NULL,
                            NULL, NULL, xdata);
    } else {
        STACK_WIND(frame, trash_common_rename_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata);
    }

out:
    return 0;
}

/**
 * The rmdir call is intercepted to avoid deletion of
 * trash directory in the mount by the user
 */
int32_t
trash_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
            dict_t *xdata)
{
    int32_t op_ret = 0;
    int32_t op_errno = 0;
    trash_private_t *priv = NULL;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    if (!check_whether_op_permitted(priv, loc)) {
        gf_log(this->name, GF_LOG_WARNING,
               "rmdir issued on %s, which is not permitted",
               priv->newtrash_dir);
        op_errno = EPERM;
        op_ret = -1;

        STACK_UNWIND_STRICT(rmdir, frame, op_ret, op_errno, NULL, NULL, xdata);
    } else {
        STACK_WIND(frame, trash_common_rmdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->rmdir, loc, flags, xdata);
    }

out:
    return 0;
}

/**
 * Volume set option is handled by the reconfigure function.
 * Here we checks whether each option is set or not ,if it
 * sets then corresponding modifciations will be made
 */
int
reconfigure(xlator_t *this, dict_t *options)
{
    uint64_t max_fsize = 0;
    int ret = 0;
    char *tmp = NULL;
    char *tmp_str = NULL;
    trash_private_t *priv = NULL;
    char trash_dir[PATH_MAX] = {
        0,
    };

    priv = this->private;

    GF_VALIDATE_OR_GOTO("trash", priv, out);

    GF_OPTION_RECONF("trash-internal-op", priv->internal, options, bool, out);
    GF_OPTION_RECONF("trash-dir", tmp, options, str, out);

    GF_OPTION_RECONF("trash", priv->state, options, bool, out);

    if (priv->state) {
        ret = create_or_rename_trash_directory(this);

        if (tmp)
            sprintf(trash_dir, "/%s/", tmp);
        else
            sprintf(trash_dir, "%s", priv->oldtrash_dir);

        if (strcmp(priv->newtrash_dir, trash_dir) != 0) {
            /* When user set a new name for trash directory, trash
             * xlator will perform a rename operation on old trash
             * directory to the new one using a STACK_WIND from here.
             * This option can be configured only when volume is in
             * started state
             */

            GF_FREE(priv->newtrash_dir);

            priv->newtrash_dir = gf_strdup(trash_dir);
            if (!priv->newtrash_dir) {
                ret = ENOMEM;
                gf_log(this->name, GF_LOG_DEBUG, "out of memory");
                goto out;
            }
            gf_log(this->name, GF_LOG_DEBUG,
                   "Renaming %s -> %s from reconfigure", priv->oldtrash_dir,
                   priv->newtrash_dir);

            if (!priv->newtrash_dir) {
                gf_log(this->name, GF_LOG_DEBUG, "out of memory");
                ret = ENOMEM;
                goto out;
            }
            ret = rename_trash_directory(this);
        }

        if (priv->internal) {
            ret = create_internalop_directory(this);
        }
    }
    tmp = NULL;

    GF_OPTION_RECONF("trash-max-filesize", max_fsize, options, size_uint64,
                     out);
    if (max_fsize) {
        priv->max_trash_file_size = max_fsize;
        gf_log(this->name, GF_LOG_DEBUG, "%" GF_PRI_SIZET " max-size",
               priv->max_trash_file_size);
    }
    GF_OPTION_RECONF("trash-eliminate-path", tmp, options, str, out);
    if (!tmp) {
        gf_log(this->name, GF_LOG_DEBUG,
               "no option specified for 'eliminate', using NULL");
    } else {
        if (priv->eliminate)
            wipe_eliminate_path(&priv->eliminate);

        tmp_str = gf_strdup(tmp);
        if (!tmp_str) {
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        ret = store_eliminate_path(tmp_str, &priv->eliminate);
    }

out:

    return ret;
}

/**
 * Notify is used to create the trash directory with fixed gfid
 * using STACK_WIND only when posix xlator is up
 */
int
notify(xlator_t *this, int event, void *data, ...)
{
    trash_private_t *priv = NULL;
    int ret = 0;

    priv = this->private;
    GF_VALIDATE_OR_GOTO("trash", priv, out);

    /* Check whether posix is up not */
    if (event == GF_EVENT_CHILD_UP) {
        if (!priv->state) {
            gf_log(this->name, GF_LOG_DEBUG, "trash xlator is off");
            goto out;
        }

        /* Here there is two possibilities ,if trash directory already
         * exist ,then we need to perform a rename operation on the
         * old one. Otherwise, we need to create the trash directory
         * For both, we need to pass location variable, gfid of parent
         * and a frame for calling STACK_WIND.The location variable
         * requires name,path,gfid and inode
         */
        if (!priv->oldtrash_dir)
            ret = create_or_rename_trash_directory(this);
        else if (strcmp(priv->newtrash_dir, priv->oldtrash_dir) != 0)
            ret = rename_trash_directory(this);
        if (ret)
            goto out;

        if (priv->internal)
            (void)create_internalop_directory(this);
    }

out:
    ret = default_notify(this, event, data);
    if (ret)
        gf_log(this->name, GF_LOG_INFO, "default notify event failed");
    return ret;
}

int32_t
mem_acct_init(xlator_t *this)
{
    int ret = -1;

    GF_VALIDATE_OR_GOTO("trash", this, out);

    ret = xlator_mem_acct_init(this, gf_trash_mt_end + 1);
    if (ret != 0) {
        gf_log(this->name, GF_LOG_ERROR,
               "Memory accounting init"
               "failed");
        return ret;
    }
out:
    return ret;
}

/**
 * trash_init
 */
int32_t
init(xlator_t *this)
{
    trash_private_t *priv = NULL;
    int ret = -1;
    char *tmp = NULL;
    char *tmp_str = NULL;
    char trash_dir[PATH_MAX] = {
        0,
    };
    uint64_t max_trash_file_size64 = 0;
    data_t *data = NULL;

    GF_VALIDATE_OR_GOTO("trash", this, out);

    if (!this->children || this->children->next) {
        gf_log(this->name, GF_LOG_ERROR,
               "not configured with exactly one child. exiting");
        ret = -1;
        goto out;
    }

    if (!this->parents) {
        gf_log(this->name, GF_LOG_WARNING, "dangling volume. check volfile");
    }

    priv = GF_CALLOC(1, sizeof(*priv), gf_trash_mt_trash_private_t);
    if (!priv) {
        gf_log(this->name, GF_LOG_ERROR, "out of memory");
        ret = ENOMEM;
        goto out;
    }

    /* Trash priv data members are initialized through the following
     * set of statements
     */
    GF_OPTION_INIT("trash", priv->state, bool, out);

    GF_OPTION_INIT("trash-dir", tmp, str, out);

    /* We store trash dir value as path for easier manipulation*/
    if (!tmp) {
        gf_log(this->name, GF_LOG_INFO,
               "no option specified for 'trash-dir', "
               "using \"/.trashcan/\"");
        priv->newtrash_dir = gf_strdup("/.trashcan/");
        if (!priv->newtrash_dir) {
            ret = ENOMEM;
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
    } else {
        sprintf(trash_dir, "/%s/", tmp);
        priv->newtrash_dir = gf_strdup(trash_dir);
        if (!priv->newtrash_dir) {
            ret = ENOMEM;
            gf_log(this->name, GF_LOG_DEBUG, "out of memory");
            goto out;
        }
    }
    tmp = NULL;

    GF_OPTION_INIT("trash-eliminate-path", tmp, str, out);
    if (!tmp) {
        gf_log(this->name, GF_LOG_INFO,
               "no option specified for 'eliminate', using NULL");
    } else {
        tmp_str = gf_strdup(tmp);
        if (!tmp_str) {
            gf_log(this->name, GF_LOG_ERROR, "out of memory");
            ret = ENOMEM;
            goto out;
        }
        ret = store_eliminate_path(tmp_str, &priv->eliminate);
    }
    tmp = NULL;

    GF_OPTION_INIT("trash-max-filesize", max_trash_file_size64, size_uint64,
                   out);
    if (!max_trash_file_size64) {
        gf_log(this->name, GF_LOG_ERROR,
               "no option specified for 'max-trashable-file-size', "
               "using default = %lld MB",
               GF_DEFAULT_MAX_FILE_SIZE / GF_UNIT_MB);
        priv->max_trash_file_size = GF_DEFAULT_MAX_FILE_SIZE;
    } else {
        priv->max_trash_file_size = max_trash_file_size64;
        gf_log(this->name, GF_LOG_DEBUG, "%" GF_PRI_SIZET " max-size",
               priv->max_trash_file_size);
    }

    GF_OPTION_INIT("trash-internal-op", priv->internal, bool, out);

    this->local_pool = mem_pool_new(trash_local_t, 64);
    if (!this->local_pool) {
        gf_log(this->name, GF_LOG_ERROR,
               "failed to create local_t's memory pool");
        ret = ENOMEM;
        goto out;
    }

    /* For creating directories inside trash with proper permissions,
     * we need to perform stat on that directories, for this we use
     * brick path
     */
    data = dict_get(this->options, "brick-path");
    if (!data) {
        gf_log(this->name, GF_LOG_ERROR,
               "no option specified for 'brick-path'");
        ret = ENOMEM;
        goto out;
    }
    priv->brick_path = gf_strdup(data->data);
    if (!priv->brick_path) {
        ret = ENOMEM;
        gf_log(this->name, GF_LOG_DEBUG, "out of memory");
        goto out;
    }

    priv->trash_itable = inode_table_new(0, this);
    gf_log(this->name, GF_LOG_DEBUG, "brick path is%s", priv->brick_path);

    this->private = (void *)priv;
    ret = 0;

out:
    if (tmp_str)
        GF_FREE(tmp_str);
    if (ret) {
        if (priv) {
            if (priv->newtrash_dir)
                GF_FREE(priv->newtrash_dir);
            if (priv->oldtrash_dir)
                GF_FREE(priv->oldtrash_dir);
            if (priv->brick_path)
                GF_FREE(priv->brick_path);
            if (priv->eliminate)
                wipe_eliminate_path(&priv->eliminate);
            GF_FREE(priv);
        }
        mem_pool_destroy(this->local_pool);
        this->local_pool = NULL;
    }
    return ret;
}

/**
 * trash_fini
 */
void
fini(xlator_t *this)
{
    trash_private_t *priv = NULL;
    inode_table_t *inode_table = NULL;

    GF_VALIDATE_OR_GOTO("trash", this, out);
    priv = this->private;
    if (priv) {
        inode_table = priv->trash_itable;
        if (priv->newtrash_dir) {
            GF_FREE(priv->newtrash_dir);
            priv->newtrash_dir = NULL;
        }
        if (priv->oldtrash_dir) {
            GF_FREE(priv->oldtrash_dir);
            priv->oldtrash_dir = NULL;
        }
        if (priv->brick_path) {
            GF_FREE(priv->brick_path);
            priv->brick_path = NULL;
        }
        if (priv->eliminate) {
            wipe_eliminate_path(&priv->eliminate);
            priv->eliminate = NULL;
        }
        if (inode_table) {
            inode_table_destroy(inode_table);
            priv->trash_itable = NULL;
        }
        GF_FREE(priv);
    }

    if (this->local_pool) {
        mem_pool_destroy(this->local_pool);
        this->local_pool = NULL;
    }
    this->private = NULL;
out:
    return;
}

struct xlator_fops fops = {
    .unlink = trash_unlink,
    .truncate = trash_truncate,
    .ftruncate = trash_ftruncate,
    .rmdir = trash_rmdir,
    .mkdir = trash_mkdir,
    .rename = trash_rename,
};

struct xlator_cbks cbks = {};

struct volume_options options[] = {
    {
        .key = {"trash"},
        .type = GF_OPTION_TYPE_BOOL,
        .default_value = "off",
        .description = "Enable/disable trash translator",
        .op_version = {GD_OP_VERSION_3_7_0},
        .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
        .tags = {"backup"},
    },
    {
        .key = {"trash-dir"},
        .type = GF_OPTION_TYPE_STR,
        .default_value = ".trashcan",
        .description = "Directory for trash files",
        .op_version = {GD_OP_VERSION_3_7_0},
        .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
        .tags = {"backup"},
    },
    {
        .key = {"trash-eliminate-path"},
        .type = GF_OPTION_TYPE_STR,
        .description = "Eliminate paths to be excluded "
                       "from trashing",
        .op_version = {GD_OP_VERSION_3_7_0},
        .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
        .tags = {"backup"},
    },
    {
        .key = {"trash-max-filesize"},
        .type = GF_OPTION_TYPE_SIZET,
        .default_value = "5MB",
        .description = "Maximum size of file that can be "
                       "moved to trash",
        .op_version = {GD_OP_VERSION_3_7_0},
        .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
        .tags = {"backup"},
    },
    {
        .key = {"trash-internal-op"},
        .type = GF_OPTION_TYPE_BOOL,
        .default_value = "off",
        .description = "Enable/disable trash translator for "
                       "internal operations",
        .op_version = {GD_OP_VERSION_3_7_0},
        .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
        .tags = {"backup"},
    },
    {.key = {"brick-path"},
     .type = GF_OPTION_TYPE_PATH,
     .default_value = "{{ brick.path }}"},
    {.key = {NULL}},
};

xlator_api_t xlator_api = {
    .init = init,
    .fini = fini,
    .notify = notify,
    .reconfigure = reconfigure,
    .mem_acct_init = mem_acct_init,
    .op_version = {1}, /* Present from the initial version */
    .fops = &fops,
    .cbks = &cbks,
    .options = options,
    .identifier = "trash",
    .category = GF_TECH_PREVIEW,
};
